//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.ComponentModel;
using System.Diagnostics.Contracts;
using System.Reflection;
using OpenLS.Core.Serialization;

namespace OpenLS.Spreadsheet
{
    ///<summary>
    /// Base class for all conditional formatting rules
    ///</summary>
#if DEBUGold
            [ContractClass(typeof(ConditionalFormattingRuleContract))]

#endif
    public abstract class ConditionalFormattingRule : IOfficeXmlSerializable, INotifyPropertyChanged
    { 
        internal static ConditionalFormattingRule ReadRule(ReadContext context, ConditionalFormatting parent)
        {
            using (ReadContext c = context.Read("cfRule"))
            {
                var Type = c.GetEnum<ConditionalFormattingType>("type");
                ConditionalFormattingRule result = GetNewRule(Type);
                result.Parent = parent;
                ((IOfficeXmlSerializable)result).ReadXml(c);
                return result;
            }
        }
        static ConditionalFormattingRule GetNewRule(ConditionalFormattingType Type)
        {
            switch (Type)
            {
                case ConditionalFormattingType.AboveAverage:
                    return new AboveAverageFormattingRule();
                case ConditionalFormattingType.BeginsWith:
                    return  new BeginsWithFormattingRule();
                case ConditionalFormattingType.CellIs:
                    return  new CellIsFormattingRule();
                case ConditionalFormattingType.ColorScale:
                    return  new ColorScaleFormattingRule();
                case ConditionalFormattingType.ContainsBlanks:
                    return  new ContainsBlankFormattingRule();
                case ConditionalFormattingType.ContainsErrors:
                    return  new ContainsErrorsFormattingRule();
                case ConditionalFormattingType.ContainsText:
                    return  new ContainsTextFormattingRule();
                case ConditionalFormattingType.DataBar:
                    return  new DataBarFormattingRule();
                case ConditionalFormattingType.DuplicateValues:
                    return  new DuplicateValuesFormattingRule();
                case ConditionalFormattingType.EndsWith:
                    return  new EndsWithFormattingRule();
                case ConditionalFormattingType.Expression:
                    return  new ExpressionFormattingRule();
                case ConditionalFormattingType.IconSet:
                    return  new IconSetFormattingRule();
                case ConditionalFormattingType.NotContainsBlanks:
                    return  new NotContainsBlanksFormattingRule();
                case ConditionalFormattingType.NotContainsErrors:
                    return  new NotContainsErrorsFormattingRule();
                case ConditionalFormattingType.NotContainsText:
                    return  new NotContainsTextFormattingRule();
                case ConditionalFormattingType.TimePeriod:
                    return  new TimePeriodFormattingRule();
                case ConditionalFormattingType.Top10:
                    return  new Top10FormattingRule();
                case ConditionalFormattingType.UniqueValues:
                    return  new UniqueValuesFormattingRule();
                default:
                    throw new NotSupportedException();
            }

        }

        internal abstract ConditionalFormattingRule Clone();

        #region IOfficeXmlSerializable Members

        internal abstract ConditionalFormattingType Type { get; }

        void IOfficeXmlSerializable.ReadXml(ReadContext context)
        {
            
           // using (ReadContext c = context.Read("cfRule"))
            {
                //Type = c.GetEnum<ConditionalFormattingType>("type");
                
                Priority = context.GetInteger("priority");
                FormatRow = context.GetOptionalBoolean("formatRow", false);
                StopIfTrue = context.GetOptionalBoolean("stopIfTrue", false);
                Text = context.GetOptionalString("text");
                stdDev = context.GetOptionalInteger("stdDev");
                EqualAverage = context.GetOptionalBoolean("equalAverage", false);

                ReadInner(context);
                Formula1 = context.ReadOptionalElementString("formula");
                Formula2 = context.ReadOptionalElementString("formula");
                Formula3 = context.ReadOptionalElementString("formula");
                Extension = context.ReadOptionalElement<ExtensionElement>("extLst");
            }
        }

        internal virtual void ReadInner(ReadContext c)
        {
        }

        void IOfficeXmlSerializable.WriteXml(WriteContext context)
        {
            using (WriteContext c = context.Write("cfRule"))
            {
                
                c.SetEnum("type", Type);
                c.SetInteger("priority", Priority);
                c.SetOptionalBoolean("formatRow", FormatRow, false);
                c.SetOptionalBoolean("stopIfTrue", StopIfTrue, false);
                c.SetOptionalString("text", Text);
                c.SetOptionalInteger("stdDev", stdDev);
                c.SetOptionalBoolean("equalAverage", EqualAverage, false);

                WriteInner(c);
                c.WriteOptionalElementString("formula", Formula1);
                c.WriteOptionalElementString("formula", Formula2);
                c.WriteOptionalElementString("formula", Formula3);
                c.WriteOptionalElement(Extension);
            }
        }

        internal virtual void WriteInner(WriteContext c)
        {
        }

        #endregion

        private ExtensionElement _extension;

        public ExtensionElement Extension
        {
            get { return _extension; }
            set { _extension = value; }
        }


        private CellFormulaBase _formula1;
        internal CellFormulaBase FormulaObject1
        {
            get
            {
                _formula1 = CellFormulaBase.Verify(_formula1, this.Worksheet);
                return _formula1;
            }
            set
            {
                _formula1 = value;
            }
        }
        internal CellFormulaBase FormulaObject2
        {
            get
            {
                _formula2 = CellFormulaBase.Verify(_formula2, this.Worksheet);
                return _formula2;
            }
            set { _formula2 = value; }
        }
        internal CellFormulaBase FormulaObject3
        {
            get
            {
                _formula3 = CellFormulaBase.Verify(_formula3, this.Worksheet);
                return _formula3;
            }
        }

        public virtual string Formula1
        {
            get
            {
                Contract.Ensures(Contract.Result<string>() == null || !Contract.Result<string>().StartsWith("="));
                return FormulaObject1 == null ? null : FormulaObject1.TextWithoutEqual;
            }
            set {
                _formula1 = CellFormulaBase.ParseFromConditionalFormatting(value, this.Worksheet);
               // _formula1 = value;
                NotifyPropertyChanged("Formula1"); }
        }
        

        internal Worksheet Worksheet
        {
            get
            {
                return this.Parent == null ? null : Parent.Worksheet;
            }
        }

        private CellFormulaBase _formula2;

        public string Formula2
        {
            get
            {
                return FormulaObject2 == null ? null : FormulaObject2.TextWithoutEqual;
            }
            set
            {
                _formula2 = CellFormulaBase.ParseFromConditionalFormatting(value, Worksheet)
                    ;NotifyPropertyChanged("Formula2");
            }
        }
        private CellFormulaBase _formula3;

        public string Formula3
        {
            get
            {
                return FormulaObject3 == null ? null : FormulaObject3.TextWithoutEqual;
            }
            set
            {
                _formula3 = CellFormulaBase.ParseFromConditionalFormatting(value, Worksheet)
                  ; NotifyPropertyChanged("Formula3");
            }
        }



        
        private int _priority;

        public int Priority
        {
            get { return _priority; }
            set { _priority = value; }
        }
        private bool _formatRow;

        internal bool FormatRow //\\ still there?
        {
            get { return _formatRow; }
            set { _formatRow = value; NotifyPropertyChanged("FormatRow");}
        }
        private bool _stopIfTrue;

        public bool StopIfTrue
        {
            get { return _stopIfTrue; }
            set { _stopIfTrue = value;NotifyPropertyChanged("StopIfTrue"); }
        }
        
       
        private string _text;

        public virtual string Text
        {
            get
            {
                return _text;
            }
            set
            {

                _text = value;NotifyPropertyChanged("Text");
            }
        }

        private int? stdDev;

        public int? StdDev
        {
            get { return stdDev; }
            set { stdDev = value;NotifyPropertyChanged("StdDev"); }
        }
        private bool equalAverage;

        public bool EqualAverage
        {
            get { return equalAverage; }
            set { equalAverage = value;NotifyPropertyChanged("EqualAverage"); }
        }

        internal ConditionalFormatting Parent
        {  get; set;
        }


        internal abstract bool Apply(Cell cell);
        internal virtual void ApplyRange(Range range){}
        
        public event PropertyChangedEventHandler PropertyChanged;
        internal void NotifyPropertyChanged (string propertyName)
        {
            Contract.Requires(GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) != null);
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        internal virtual void PrepareBeforeWrite(ConditionalFormatting conditionalFormatting, FormatType formatType)
        {
            Contract.Ensures(formatType != FormatType.Biff || 
                this.FormulaObject1 != null
                || (this is ColorScaleFormattingRule)
                || (this is DataBarFormattingRule)
                || (this is IconSetFormattingRule)
                );

        }
#if DEBUGold
        [ContractClassFor(typeof (ConditionalFormattingRule))]
        abstract class ConditionalFormattingRuleContract: ConditionalFormattingRule
        {
            internal override void PrepareBeforeWrite(ConditionalFormatting conditionalFormatting, FormatType formatType)
            {
                Contract.Ensures(formatType != FormatType.Biff || this.FormulaObject1 != null);
                throw new NotSupportedException();
            }
        }
#endif

        internal enum FormatType
        {
            Biff,
            Default,
        }

        static string EscapeText(string text)
        {
            if (text == null)
                return null;
            return text.Replace("\"", "\"\"");
        }

        internal string EscapedText { get { return EscapeText(Text); }}
    }
}
